// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/operator-properties.h"

#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/opcodes.h"
#include "src/runtime/runtime.h"

#include "src/objects-inl.h" // weolar

namespace v8 {
namespace internal {
    namespace compiler {

        // static
        bool OperatorProperties::HasContextInput(const Operator* op)
        {
            IrOpcode::Value opcode = static_cast<IrOpcode::Value>(op->opcode());
            return IrOpcode::IsJsOpcode(opcode);
        }

        // static
        bool OperatorProperties::NeedsExactContext(const Operator* op)
        {
            DCHECK(HasContextInput(op));
            IrOpcode::Value const opcode = static_cast<IrOpcode::Value>(op->opcode());
            switch (opcode) {
#define CASE(Name) case IrOpcode::k##Name:
                // Binary/unary operators, calls and constructor calls only
                // need the context to generate exceptions or lookup fields
                // on the native context, so passing any context is fine.
                JS_SIMPLE_BINOP_LIST(CASE)
                JS_CALL_OP_LIST(CASE)
                JS_CONSTRUCT_OP_LIST(CASE)
                JS_SIMPLE_UNOP_LIST(CASE)
#undef CASE
            case IrOpcode::kJSCloneObject:
            case IrOpcode::kJSCreate:
            case IrOpcode::kJSCreateLiteralArray:
            case IrOpcode::kJSCreateEmptyLiteralArray:
            case IrOpcode::kJSCreateLiteralObject:
            case IrOpcode::kJSCreateEmptyLiteralObject:
            case IrOpcode::kJSCreateArrayFromIterable:
            case IrOpcode::kJSCreateLiteralRegExp:
            case IrOpcode::kJSForInEnumerate:
            case IrOpcode::kJSForInNext:
            case IrOpcode::kJSForInPrepare:
            case IrOpcode::kJSGeneratorRestoreContext:
            case IrOpcode::kJSGeneratorRestoreContinuation:
            case IrOpcode::kJSGeneratorRestoreInputOrDebugPos:
            case IrOpcode::kJSGeneratorRestoreRegister:
            case IrOpcode::kJSGetSuperConstructor:
            case IrOpcode::kJSLoadGlobal:
            case IrOpcode::kJSLoadMessage:
            case IrOpcode::kJSStackCheck:
            case IrOpcode::kJSStoreGlobal:
            case IrOpcode::kJSStoreMessage:
                return false;

            case IrOpcode::kJSCallRuntime:
                return Runtime::NeedsExactContext(CallRuntimeParametersOf(op).id());

            case IrOpcode::kJSCreateArguments:
                // For mapped arguments we need to access slots of context-allocated
                // variables if there's aliasing with formal parameters.
                return CreateArgumentsTypeOf(op) == CreateArgumentsType::kMappedArguments;

            case IrOpcode::kJSCreateBlockContext:
            case IrOpcode::kJSCreateClosure:
            case IrOpcode::kJSCreateFunctionContext:
            case IrOpcode::kJSCreateGeneratorObject:
            case IrOpcode::kJSCreateCatchContext:
            case IrOpcode::kJSCreateWithContext:
            case IrOpcode::kJSDebugger:
            case IrOpcode::kJSDeleteProperty:
            case IrOpcode::kJSGeneratorStore:
            case IrOpcode::kJSHasProperty:
            case IrOpcode::kJSLoadContext:
            case IrOpcode::kJSLoadModule:
            case IrOpcode::kJSLoadNamed:
            case IrOpcode::kJSLoadProperty:
            case IrOpcode::kJSStoreContext:
            case IrOpcode::kJSStoreDataPropertyInLiteral:
            case IrOpcode::kJSStoreInArrayLiteral:
            case IrOpcode::kJSStoreModule:
            case IrOpcode::kJSStoreNamed:
            case IrOpcode::kJSStoreNamedOwn:
            case IrOpcode::kJSStoreProperty:
                return true;

            case IrOpcode::kJSAsyncFunctionEnter:
            case IrOpcode::kJSAsyncFunctionReject:
            case IrOpcode::kJSAsyncFunctionResolve:
            case IrOpcode::kJSCreateArrayIterator:
            case IrOpcode::kJSCreateAsyncFunctionObject:
            case IrOpcode::kJSCreateBoundFunction:
            case IrOpcode::kJSCreateCollectionIterator:
            case IrOpcode::kJSCreateIterResultObject:
            case IrOpcode::kJSCreateStringIterator:
            case IrOpcode::kJSCreateKeyValueArray:
            case IrOpcode::kJSCreateObject:
            case IrOpcode::kJSCreatePromise:
            case IrOpcode::kJSCreateTypedArray:
            case IrOpcode::kJSCreateArray:
            case IrOpcode::kJSFulfillPromise:
            case IrOpcode::kJSObjectIsArray:
            case IrOpcode::kJSPerformPromiseThen:
            case IrOpcode::kJSPromiseResolve:
            case IrOpcode::kJSRegExpTest:
            case IrOpcode::kJSRejectPromise:
            case IrOpcode::kJSResolvePromise:
                // These operators aren't introduced by BytecodeGraphBuilder and
                // thus we don't bother checking them. If you ever introduce one
                // of these early in the BytecodeGraphBuilder make sure to check
                // whether they are context-sensitive.
                break;

#define CASE(Name) case IrOpcode::k##Name:
                // Non-JavaScript operators don't have a notion of "context"
                COMMON_OP_LIST(CASE)
                CONTROL_OP_LIST(CASE)
                MACHINE_OP_LIST(CASE)
                MACHINE_SIMD_OP_LIST(CASE)
                SIMPLIFIED_OP_LIST(CASE)
                break;
#undef CASE
            }
            UNREACHABLE();
        }

        // static
        bool OperatorProperties::HasFrameStateInput(const Operator* op)
        {
            switch (op->opcode()) {
            case IrOpcode::kCheckpoint:
            case IrOpcode::kFrameState:
                return true;
            case IrOpcode::kJSCallRuntime: {
                const CallRuntimeParameters& p = CallRuntimeParametersOf(op);
                return Linkage::NeedsFrameStateInput(p.id());
            }

            // Strict equality cannot lazily deoptimize.
            case IrOpcode::kJSStrictEqual:
                return false;

            // Generator creation cannot call back into arbitrary JavaScript.
            case IrOpcode::kJSCreateGeneratorObject:
                return false;

            // Binary operations
            case IrOpcode::kJSAdd:
            case IrOpcode::kJSSubtract:
            case IrOpcode::kJSMultiply:
            case IrOpcode::kJSDivide:
            case IrOpcode::kJSModulus:
            case IrOpcode::kJSExponentiate:

            // Bitwise operations
            case IrOpcode::kJSBitwiseOr:
            case IrOpcode::kJSBitwiseXor:
            case IrOpcode::kJSBitwiseAnd:

            // Shift operations
            case IrOpcode::kJSShiftLeft:
            case IrOpcode::kJSShiftRight:
            case IrOpcode::kJSShiftRightLogical:

            // Compare operations
            case IrOpcode::kJSEqual:
            case IrOpcode::kJSGreaterThan:
            case IrOpcode::kJSGreaterThanOrEqual:
            case IrOpcode::kJSLessThan:
            case IrOpcode::kJSLessThanOrEqual:
            case IrOpcode::kJSHasProperty:
            case IrOpcode::kJSHasInPrototypeChain:
            case IrOpcode::kJSInstanceOf:
            case IrOpcode::kJSOrdinaryHasInstance:

            // Object operations
            case IrOpcode::kJSCreate:
            case IrOpcode::kJSCreateArguments:
            case IrOpcode::kJSCreateArray:
            case IrOpcode::kJSCreateTypedArray:
            case IrOpcode::kJSCreateLiteralArray:
            case IrOpcode::kJSCreateArrayFromIterable:
            case IrOpcode::kJSCreateLiteralObject:
            case IrOpcode::kJSCreateLiteralRegExp:
            case IrOpcode::kJSCreateObject:
            case IrOpcode::kJSCloneObject:

            // Property access operations
            case IrOpcode::kJSLoadNamed:
            case IrOpcode::kJSStoreNamed:
            case IrOpcode::kJSLoadProperty:
            case IrOpcode::kJSStoreProperty:
            case IrOpcode::kJSLoadGlobal:
            case IrOpcode::kJSStoreGlobal:
            case IrOpcode::kJSStoreNamedOwn:
            case IrOpcode::kJSStoreDataPropertyInLiteral:
            case IrOpcode::kJSDeleteProperty:

            // Conversions
            case IrOpcode::kJSToLength:
            case IrOpcode::kJSToName:
            case IrOpcode::kJSToNumber:
            case IrOpcode::kJSToNumberConvertBigInt:
            case IrOpcode::kJSToNumeric:
            case IrOpcode::kJSToObject:
            case IrOpcode::kJSToString:
            case IrOpcode::kJSParseInt:

            // Call operations
            case IrOpcode::kJSConstructForwardVarargs:
            case IrOpcode::kJSConstruct:
            case IrOpcode::kJSConstructWithArrayLike:
            case IrOpcode::kJSConstructWithSpread:
            case IrOpcode::kJSCallForwardVarargs:
            case IrOpcode::kJSCall:
            case IrOpcode::kJSCallWithArrayLike:
            case IrOpcode::kJSCallWithSpread:

            // Misc operations
            case IrOpcode::kJSAsyncFunctionEnter:
            case IrOpcode::kJSAsyncFunctionReject:
            case IrOpcode::kJSAsyncFunctionResolve:
            case IrOpcode::kJSForInEnumerate:
            case IrOpcode::kJSForInNext:
            case IrOpcode::kJSStackCheck:
            case IrOpcode::kJSDebugger:
            case IrOpcode::kJSGetSuperConstructor:
            case IrOpcode::kJSBitwiseNot:
            case IrOpcode::kJSDecrement:
            case IrOpcode::kJSIncrement:
            case IrOpcode::kJSNegate:
            case IrOpcode::kJSPromiseResolve:
            case IrOpcode::kJSRejectPromise:
            case IrOpcode::kJSResolvePromise:
            case IrOpcode::kJSPerformPromiseThen:
            case IrOpcode::kJSObjectIsArray:
            case IrOpcode::kJSRegExpTest:
                return true;

            default:
                return false;
            }
        }

        // static
        int OperatorProperties::GetTotalInputCount(const Operator* op)
        {
            return op->ValueInputCount() + GetContextInputCount(op) + GetFrameStateInputCount(op) + op->EffectInputCount() + op->ControlInputCount();
        }

        // static
        bool OperatorProperties::IsBasicBlockBegin(const Operator* op)
        {
            Operator::Opcode const opcode = op->opcode();
            return opcode == IrOpcode::kStart || opcode == IrOpcode::kEnd || opcode == IrOpcode::kDead || opcode == IrOpcode::kLoop || opcode == IrOpcode::kMerge || opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse || opcode == IrOpcode::kIfSuccess || opcode == IrOpcode::kIfException || opcode == IrOpcode::kIfValue || opcode == IrOpcode::kIfDefault;
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
